package br.com.ia.controle;

import br.com.ia.bean.Aresta;
import br.com.ia.bean.Host;
import java.util.HashMap;
import java.util.LinkedList;

/**
 *
 * @author Jonas Cezar
 */
public class Buscas implements Runnable {

    //Variaveis Staticas
    private final static int TYPE_PROFUNDIDADE = 1;
    private final static int TYPE_LARGURA = 2;
    //Variaveis Globais
    private HashMap<String, Host> hosts;
    private LinkedList<Host> caminhoPercorrido;
    private Host hostInicio, hostAlvo;
    private StringBuilder resultado;
    private int saltos;
    private int custos;
    private int type;
    private javax.swing.JTextArea jTextAreaResultado;

    public void buscaEmProfundidade(Host hostInicio, Host hostAlvo, javax.swing.JTextArea jTextAreaResultado) {

        this.hostInicio = hostInicio;
        this.hostAlvo = hostAlvo;
        this.jTextAreaResultado = jTextAreaResultado;

        type = TYPE_PROFUNDIDADE;

        synchronized (this) {
            this.notify();
        }
    }

    public void buscaEmLargura(Host hostInicio, Host hostAlvo, javax.swing.JTextArea jTextAreaResultado) {

        this.hostInicio = hostInicio;
        this.hostAlvo = hostAlvo;
        this.jTextAreaResultado = jTextAreaResultado;

        type = TYPE_LARGURA;

        synchronized (this) {
            this.notify();
        }
    }

    private void addRelatorio(String relatorio) {

        System.out.println(relatorio);
        resultado.append(relatorio).append("\n");
        jTextAreaResultado.setText(resultado.toString());

    }

    private void inicializa() {

        resultado = new StringBuilder();
        caminhoPercorrido = new LinkedList<Host>();
        custos = 0;
        saltos = 0;

        GeraGrafo.zeraStatus();
        GeraGrafo.setNodeStatus(hostInicio.getRotulo(), GeraGrafo.STATUS_NODE_INICIAL);
        GeraGrafo.setNodeStatus(hostAlvo.getRotulo(), GeraGrafo.STATUS_NODE_FINAL);

    }

    private void emProfundidade() {

        inicializa();

        addRelatorio("Busca em Profundidade.\n");

        hostInicio = hosts.get(hostInicio.getRotulo());

        addRelatorio("Ponto de Partida > " + hostInicio.getRotulo() + ".");

        caminhoPercorrido.add(hostInicio);

        LinkedList<Aresta> arestas = new LinkedList();

        if (hostInicio.equals(hostAlvo)) {

            addRelatorio("Host alvo encontrado.");
            addRelatorio("Custos = " + custos + ".");
            addRelatorio("Saltos = " + saltos + ".");

        } else {
            arestas.addAll(hostInicio.getArestas());
        }

        Chronometer.start();

        while (!arestas.isEmpty()) {

            //Retira a primeira Aresta em joga em 'a'
            Aresta a = (Aresta) arestas.pollFirst();

            //Se o host ainda não foi visitado
            if (!caminhoPercorrido.contains(a.getHost())) {

                addRelatorio("> " + a.getHost().getRotulo() + " Custo " + a.getCusto());

                caminhoPercorrido.add(a.getHost());

                custos += a.getCusto();
                saltos++;

                if (a.getHost().equals(hostAlvo)) {

                    addRelatorio("Host alvo encontrado.");
                    addRelatorio("Custos = " + custos + ".");
                    addRelatorio("Saltos = " + saltos + ".");

                    break;
                }

                GeraGrafo.setNodeStatus(a.getHost().getRotulo(), GeraGrafo.STATUS_NODE_VISITADO);

                arestas.addAll(0, a.getHost().getArestas());

            }

        }

        Chronometer.stop();

        addRelatorio("Tempo gasto = " + Chronometer.hmsm());

        addRelatorio("Fim da Busca.");

    }

    private void emLargura() {

        inicializa();

        addRelatorio("Busca em Largura.\n");

        hostInicio = hosts.get(hostInicio.getRotulo());

        addRelatorio("Ponto de Partida > " + hostInicio.getRotulo() + ".");

        caminhoPercorrido.add(hostInicio);


        LinkedList<Aresta> arestas = new LinkedList();

        if (hostInicio.equals(hostAlvo)) {

            addRelatorio("Host alvo encontrado.");
            addRelatorio("Custos = " + custos + ".");
            addRelatorio("Saltos = " + saltos + ".");

        } else {
            arestas.addAll(hostInicio.getArestas());
        }

        Chronometer.start();

        while (!arestas.isEmpty()) {

            Aresta a = (Aresta) arestas.pollFirst();

            if (!caminhoPercorrido.contains(a.getHost())) {

                addRelatorio("> " + a.getHost().getRotulo() + " Custo " + a.getCusto());

                caminhoPercorrido.add(a.getHost());
                custos += a.getCusto();
                saltos++;

                if (a.getHost().equals(hostAlvo)) {

                    addRelatorio("Host alvo encontrado.");
                    addRelatorio("Custos = " + custos + ".");
                    addRelatorio("Saltos = " + saltos + ".");

                    break;
                }

                GeraGrafo.setNodeStatus(a.getHost().getRotulo(), GeraGrafo.STATUS_NODE_VISITADO);

                arestas.addAll(arestas.size(), a.getHost().getArestas());

            }

        }

        Chronometer.stop();

        addRelatorio("Tempo gasto = " + Chronometer.hmsm());

        addRelatorio("Fim da Busca.");

    }

    public HashMap<String, Host> getHosts() {
        return hosts;
    }

    public void setHosts(HashMap<String, Host> hosts) {
        this.hosts = hosts;
    }

    public LinkedList<Host> getListaCaminho() {
        return caminhoPercorrido;
    }

    public void setListaCaminho(LinkedList<Host> caminhoPercorrido) {
        this.caminhoPercorrido = caminhoPercorrido;
    }

    public StringBuilder getResultado() {
        return resultado;
    }

    public Host getHostAlvo() {
        return hostAlvo;
    }

    public Host getInicio() {
        return hostInicio;
    }

    @Override
    public void run() {

        while (true) {

            if (type == TYPE_PROFUNDIDADE) {
                emProfundidade();
            } else if (type == TYPE_LARGURA) {
                emLargura();
            }

            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException ex) {
                    System.out.println(ex.getMessage());
                }
            }
        }

    }
}
